키(Key) 설정을 통한 데이터 인스턴스(Instance) 구분 전략

키(Key) 설정을 통한 데이터 인스턴스(Instance) 구분 전략

데이터 분산 서비스(DDS, Data Distribution Service) 아키텍처의 정수는 ‘데이터 중심(Data-Centric)’ 사상에 있다. 이는 네트워크상에서 메시지를 단순히 전달하는 행위를 넘어, 정보의 상태(State)와 생명주기(Lifecycle)를 시스템 차원에서 관리한다는 것을 의미한다. 이러한 관리 체계의 근간이 되는 것이 바로 ’키(Key)’와 ’인스턴스(Instance)’의 개념이다. 본 장에서는 DDS 미들웨어상에서 키를 정의하여 인스턴스를 구분하는 전략을 심층적으로 다룬다. IDL(Interface Definition Language)을 통한 키 명세 방법부터, 인스턴스의 생성과 소멸을 다루는 복잡한 생명주기 관리 메커니즘, QoS(Quality of Service) 정책과의 상호작용, 그리고 대규모 분산 시스템에서의 성능 최적화를 위한 고급 키 설계 패턴까지 포괄적으로 논의한다.

1. 데이터 중심 아키텍처(DCA)와 식별성(Identity)의 본질

DDS 사양(Specification)에서 토픽(Topic)이 데이터의 ’형식(Type)’과 ’의미’를 정의하는 스키마(Schema)라면, 인스턴스(Instance)는 그 형식을 따르는 실체적인 데이터 객체를 의미한다.1 관계형 데이터베이스(RDBMS)의 비유를 빌리자면, 토픽은 테이블(Table)의 정의에 해당하고, 인스턴스는 테이블 내의 각 행(Row)에 해당하며, 키(Key)는 그 행을 유일하게 식별하는 기본 키(Primary Key)와 동일한 역할을 수행한다.2

1.1 토픽, 키, 인스턴스의 계층적 관계

DDS 통신 모델에서 데이터는 ‘샘플(Sample)’, ‘인스턴스(Instance)’, ’토픽(Topic)’의 계층 구조를 가진다. 샘플은 특정 시점에 측정된 데이터의 값(Value)이며, 인스턴스는 시간의 흐름에 따라 변화하는 데이터 객체의 흐름(Stream) 그 자체이다. 토픽은 이러한 인스턴스들의 집합을 규정하는 틀이다.1

예를 들어 ’항공기 위치(FlightTrack)’라는 토픽이 존재한다고 가정하자. 이 토픽은 위도, 경도, 고도, 속도 등의 속성을 가진다. 그러나 하늘에는 수천 대의 항공기가 동시에 비행하고 있다. 시스템이 단순히 ’위치 데이터’를 수신하는 것에 그치지 않고, ’대한항공 101편’의 궤적과 ’아시아나 202편’의 궤적을 논리적으로 분리하여 추적하기 위해서는 각 데이터를 식별할 수 있는 고유한 꼬리표가 필요하다. 이때 항공편명(Flight ID)이나 호출 부호(Call Sign)가 키(Key)가 되며, 특정 키 값을 가진 데이터의 흐름이 바로 인스턴스가 된다.4

이러한 인스턴스 기반의 구분은 단순한 데이터 필터링 이상의 의미를 가진다. 미들웨어는 각 인스턴스를 마치 독립적인 통신 채널처럼 취급한다. 즉, ‘KE101’ 인스턴스의 데이터 업데이트는 ‘OZ202’ 인스턴스의 상태나 이력(History)에 전혀 영향을 주지 않는다. 이는 개발자가 별도의 코드를 작성하지 않아도 미들웨어 레벨에서 다중 객체의 동시 추적과 상태 관리를 자동화할 수 있음을 시사한다.5

1.2 글로벌 데이터 공간(Global Data Space)과 파티셔닝

DDS는 네트워크상에 분산된 노드들이 마치 하나의 공유 메모리에 접근하는 것과 같은 환상을 제공하는 ‘글로벌 데이터 공간’ 개념을 표방한다. 키는 이 거대한 데이터 공간을 논리적으로 파티셔닝(Partitioning)하는 핵심 도구이다. RDBMS가 인덱스(Index)를 통해 데이터 검색 속도를 높이듯, DDS 미들웨어는 내부적으로 키 값을 해싱(Hashing)하여 인스턴스별로 메모리 공간을 할당하고 관리한다.

키가 없는 토픽(Keyless Topic)은 시스템 내에서 단 하나의 인스턴스(Singleton)만을 가지는 것으로 간주된다. 반면, 키가 정의된 토픽(Keyed Topic)은 키의 가능한 값의 조합만큼 무수히 많은 인스턴스를 가질 수 있다. 이는 시스템의 확장성(Scalability)과 직결된다. 수백만 개의 센서 데이터를 처리해야 하는 IoT 시스템에서 적절한 키 설계는 데이터를 효율적으로 분산하고 조회하는 성능의 척도가 된다.6

2. IDL 기반의 키 정의와 타입 시스템

DDS에서 키는 런타임에 임의로 지정하는 것이 아니라, 컴파일 타임에 데이터 타입 정의 언어인 IDL을 통해 명시적으로 선언된다. 이는 시스템 전체의 타입 안정성(Type Safety)을 보장하기 위함이다. OMG DDS 사양 및 XTypes(Extensible and Dynamic Topic Types) 사양은 @key 어노테이션(Annotation)을 사용하여 구조체(struct) 내의 필드를 키로 지정하는 표준 방법을 제공한다.7

2.1 단순 키(Simple Key)와 복합 키(Composite Key) 구성 전략

가장 기본적인 키 설정은 단일 필드를 키로 지정하는 것이다. 정수형(Integer), 문자열(String), 또는 열거형(Enum) 등 스칼라 타입이 주로 사용된다.

IDL 예시: 단일 키 정의

struct SensorData {
@key long sensor_id; // 센서 고유 번호가 키가 된다
double temperature;
double humidity;
};

위 예시에서 sensor_id는 유일성을 보장하는 식별자이다. 미들웨어는 이 sensor_id 값이 다르면 서로 다른 센서 인스턴스로 인식한다.

그러나 실세계의 복잡한 도메인에서는 단일 필드만으로 객체를 유일하게 식별하기 어려운 경우가 많다. 예를 들어, 동일한 센서 ID를 가진 장비가 여러 지역(Zone)에 설치될 수 있다. 이 경우 복수의 필드에 @key 어노테이션을 부여하여 복합 키(Composite Key)를 구성해야 한다.8

IDL 예시: 복합 키 정의

struct SmartMeter {
@key string region_code; // 예: "SEOUL_GN"
@key long meter_id;          // 예: 1001
float power_usage;
};

복합 키를 사용할 경우, DDS 미들웨어는 지정된 모든 키 필드의 값을 조합하여 하나의 고유 식별자로 취급한다. 즉, region_code가 같더라도 meter_id가 다르면 다른 인스턴스이며, meter_id가 같더라도 region_code가 다르면 역시 다른 인스턴스이다. 내부적으로 미들웨어는 이 필드들을 직렬화(Serialization)한 후, 128비트(16바이트) 길이의 MD5 해시 등을 생성하여 내부 키 해시(Internal Key Hash)로 변환하고, 이를 인덱싱 키로 사용한다.10

2.2 중첩된 타입(Nested Type)과 재귀적 키 정의

객체 지향 모델링이나 복잡한 데이터 구조를 표현하기 위해 구조체가 다른 구조체를 멤버로 포함하는 중첩 구조(Nested Struct)가 흔히 사용된다. 이때 키의 정의는 재귀적(Recursive)으로 적용된다.9

규칙은 다음과 같다:

  1. 상위 구조체의 멤버 필드에 @key가 붙어 있으면, 그 멤버 자체가 키의 일부가 된다.
  2. 만약 그 멤버가 구조체 타입이고, 그 하위 구조체 내부에도 @key 필드가 정의되어 있다면, 하위 구조체의 키 필드들이 상위 구조체의 키 구성요소로 승격되어 포함된다.
  3. 상위 구조체 멤버에 @key가 없더라도, 하위 구조체 내에 @key가 있다면, 이는 상위 구조체의 인스턴스 구분에 영향을 주지 않는다. 즉, 키는 반드시 최상위 수준에서부터 명시적으로 포함 관계가 선언되어야 한다. (단, 일부 구현체나 XTypes 버전에 따라 해석이 다를 수 있으므로, 키로 사용하고자 하는 중첩 멤버에는 명시적으로 @key를 붙이는 것이 안전하다).8

IDL 예시: 중첩 구조체와 키

struct DeviceId {
@key string serial_number;
@key string manufacturer;
};

struct RobotStatus {
@key DeviceId id; // 중첩된 구조체를 키로 사용
double battery_level;
long uptime;
};

위 예시에서 RobotStatus의 인스턴스를 식별하는 키는 id.serial_numberid.manufacturer의 조합이다. 이는 도메인 주도 설계(DDD)에서 ’값 객체(Value Object)’를 식별자로 사용하는 패턴과 유사하며, 데이터 모델의 모듈화를 촉진한다.

2.3 상속(Inheritance)과 키의 확장성

DDS XTypes 사양은 객체 지향 언어의 상속 개념을 지원한다. 구조체 간 상속이 발생할 때, 키의 정의 또한 상속된다. 파생(Derived) 타입은 기본(Base) 타입이 가진 모든 키 필드를 그대로 물려받으며, 필요하다면 자신의 멤버에 추가적인 @key를 정의하여 식별자를 확장할 수 있다.12

IDL 예시: 상속과 키 확장

struct Asset {
@key string asset_id;
};

struct Vehicle : Asset { // Asset의 asset_id를 키로 상속받음
string model_name;
};

struct ElectricVehicle : Vehicle {
@key string battery_id; // 키 확장: asset_id + battery_id 조합이 키가 됨
float charge_level;
};

이러한 상속 구조에서 ElectricVehicle 인스턴스는 asset_idbattery_id가 모두 같아야 동일한 인스턴스로 취급된다. 이는 데이터 모델을 계층적으로 설계할 때 유연성을 제공한다. 그러나 과도한 상속과 키 확장은 시스템의 복잡도를 높이고, 이종 기종 간(예: C++와 Java 어플리케이션 간) 상호 운용성에서 타입 매핑 오류를 유발할 수 있으므로 신중해야 한다.

2.4 키 설정 시 제약사항 및 주의점

모든 데이터 타입이 키가 될 수 있는 것은 아니다. DDS 사양 및 구현체별 제약사항을 고려해야 한다.

  • 가변 길이 타입의 제한: 일부 구현체는 무제한 문자열(string)이나 시퀀스(sequence)를 키로 사용하는 것을 비권장하거나, 성능상의 이유로 최대 길이를 제한할 것을 요구한다. 키 필드는 내부적으로 고정 크기의 버퍼나 해시로 변환되어야 효율적인 검색이 가능하기 때문이다. 가능하면 string과 같이 상한(Bound)을 명시하는 것이 좋다.3
  • 유니온(Union) 및 배열: 유니온 타입의 경우 판별자(Discriminator)가 키가 될 수 있는지, 혹은 유니온 멤버가 키가 될 수 있는지에 대한 지원 여부가 구현체마다 상이할 수 있다. 일반적으로는 구조체 내의 스칼라 필드나 고정 크기 배열을 키로 사용하는 것이 가장 안전하고 호환성이 높다.
  • 불변성(Immutability): 키로 지정된 필드의 값은 인스턴스의 생명주기 동안 절대 변경되어서는 안 된다. 만약 키 값을 변경해야 한다면, 그것은 논리적으로 기존 인스턴스를 삭제(Dispose)하고 새로운 키를 가진 인스턴스를 생성(Register)하는 행위로 간주된다. 따라서 시간에 따라 변할 수 있는 속성(예: 사용자의 등급, 차량의 도색 색상)은 키로 설정해서는 안 된다.6

3. 인스턴스 생명주기(Lifecycle)와 상태 전이 메커니즘

키를 통해 인스턴스가 구분되면, DDS 미들웨어는 각 인스턴스의 탄생부터 죽음까지의 생명주기를 관리하는 정교한 상태 머신(State Machine)을 작동시킨다. 이는 단순한 데이터 전송을 넘어, 분산 시스템 내에서 객체의 존재 유무와 유효성을 판단하는 기준이 된다. 인스턴스의 상태는 크게 ALIVE, NOT_ALIVE_DISPOSED, NOT_ALIVE_NO_WRITERS의 세 가지 상태로 정의되며, 이는 InstanceStateKind를 통해 어플리케이션에 노출된다.3

아래 표는 DDS 인스턴스의 상태 종류와 그 의미를 요약한 것이다.

인스턴스 상태 (Instance State)의미 (Semantics)발생 조건
ALIVE인스턴스가 존재하며 활성 상태임DataWriter가 write() 또는 register_instance() 호출.
NOT_ALIVE_DISPOSED인스턴스가 명시적으로 폐기됨DataWriter가 dispose() 호출. 데이터가 논리적으로 삭제됨.
NOT_ALIVE_NO_WRITERS인스턴스를 관리하는 작성자가 없음모든 DataWriter가 unregister() 했거나 연결이 끊김(Liveliness 상실).

3.1 ALIVE 상태: 인스턴스의 활성화

인스턴스가 처음 생성되거나 데이터가 업데이트되고 있는 정상적인 상태이다.

  • 생성 시점: DataWriter가 특정 키 값을 가진 데이터를 처음으로 write() 하거나 register_instance()를 호출하면, 해당 인스턴스는 시스템에 등록되고 ALIVE 상태가 된다.16
  • 활성 유지: DataWriter가 지속적으로 데이터를 발행하거나, LIVELINESS QoS 정책에 따라 주기적으로 하트비트(Heartbeat)를 보내면 이 상태가 유지된다.
  • DataReader 측면: DataReader가 데이터를 수신했을 때 SampleInfoinstance_stateALIVE라면, 해당 데이터는 유효한 최신 정보임을 의미하며 valid_data 플래그가 TRUE로 설정된다.17

3.2 NOT_ALIVE_DISPOSED 상태: 논리적 삭제

이 상태는 데이터가 더 이상 유효하지 않거나, 해당 객체가 시스템에서 의도적으로 제거되었음을 의미한다. 데이터베이스의 DELETE 연산과 개념적으로 유사하다.

  • 발생 메커니즘: DataWriter가 dispose() API를 호출하면 발생한다. 이 명령은 네트워크를 통해 모든 연결된 DataReader에게 전파된다.3
  • 사용 사례: 비행 추적 시스템에서 비행기가 착륙하여 레이더 추적을 종료해야 할 때, 주식 거래 시스템에서 장이 마감되어 호가 창을 닫아야 할 때 사용된다.
  • 데이터 처리: 이 상태로 전이될 때 DataReader는 실제 데이터 값(Payload)이 없는 메타데이터 샘플을 수신할 수 있다. 이때 valid_data 플래그는 FALSE가 되며, 어플리케이션은 instance_state를 확인하여 해당 객체에 대한 리소스를 정리하거나 UI에서 아이콘을 삭제하는 등의 후속 처리를 수행해야 한다.3

3.3 NOT_ALIVE_NO_WRITERS 상태: 소유권 상실 및 부재

이 상태는 인스턴스가 논리적으로 삭제된 것은 아니지만, 현재 시스템 내에 해당 인스턴스의 상태를 업데이트해 줄 주체(Active Writer)가 아무도 없음을 나타낸다. ‘관리자 부재’ 상태로 이해할 수 있다.

  • 발생 메커니즘:
  1. 해당 인스턴스를 쓰던 모든 DataWriter가 unregister_instance()를 호출하여 관리를 포기한 경우.
  2. DataWriter 어플리케이션이 비정상 종료(Crash)되거나 네트워크가 단절되어 LIVELINESS QoS 타임아웃이 발생한 경우.18
  3. DataWriter 객체 자체가 delete_datawriter()에 의해 삭제된 경우.
  • 의미: 데이터의 유효성은 남아있을 수 있으나, 더 이상 새로운 업데이트는 기대할 수 없다. 시스템 설계에 따라 이 상태를 ’일시적 통신 두절’로 해석하여 마지막 값을 유지할지, 아니면 ’신뢰성 상실’로 간주하여 데이터를 화면에서 지울지 결정해야 한다.

3.4 명시적 등록(Register), 해제(Unregister), 폐기(Dispose)의 차이

DDS 개발자가 가장 혼동하기 쉬운 것이 unregisterdispose의 차이이다. 이 둘은 명확히 다른 의미를 가진다.4

  • register_instance(): 미들웨어에 “내가 이 인스턴스의 데이터를 쓸 것이다“라고 선언하고 자원을 예약하는 행위이다. 반환값으로 핸들(InstanceHandle_t)을 제공하여 이후의 쓰기 성능을 최적화한다.
  • unregister_instance(): “나는 더 이상 이 인스턴스에 관여하지 않겠다“는 선언이다. 이는 ’나(DataWriter)’와 ‘인스턴스’ 사이의 관계만 끊는 것이지, 인스턴스 자체를 파괴하는 것은 아니다. 다른 DataWriter가 여전히 그 인스턴스를 쓰고 있다면 인스턴스는 계속 ALIVE 상태로 남는다. 만약 내가 마지막 작성자였다면 NOT_ALIVE_NO_WRITERS가 된다.
  • dispose(): “이 인스턴스는 세상에서 사라졌다“는 전역적 선언이다. 누가 쓰고 있었는지와 무관하게 인스턴스 상태를 NOT_ALIVE_DISPOSED로 전이시킨다.

중요: 오토 디스포즈(Auto-Dispose) QoS 설정

WRITER_DATA_LIFECYCLE QoS 정책의 autodispose_unregistered_instances 속성은 시스템의 거동을 크게 바꿀 수 있다.

  • TRUE (기본값): DataWriter가 unregister하거나 삭제될 때, 자동으로 dispose 메시지를 함께 보낸다. 즉, 작성자가 사라지면 데이터도 함께 폐기 처리된다. 이는 작성자와 데이터의 운명을 같이 묶는 것으로, 단순한 시스템에서는 편리하지만, 소유권이 이관되어야 하는 고가용성(HA) 시스템에서는 데이터 유실을 초래할 수 있다.
  • FALSE: 작성자가 사라져도 데이터는 NOT_ALIVE_NO_WRITERS 상태로 남아 있는다. DURABILITY QoS가 설정되어 있다면, 나중에 늦게 합류한(Late-joining) DataReader도 이 데이터를 조회할 수 있다. 복잡한 분산 시스템에서는 이 설정이 더 안전할 수 있다.19

4. 인스턴스 핸들(Instance Handle)과 고성능 I/O 전략

키 기반 통신은 필연적으로 오버헤드를 동반한다. DataWriter가 write()를 호출할 때마다 미들웨어는 데이터 샘플 내의 키 필드 값을 추출하고, 직렬화하여, 해시를 계산하고, 내부 인덱스(Map)를 검색하여 해당 인스턴스의 메모리 블록을 찾아야 한다. 초당 수만 건 이상의 데이터를 처리해야 하는 고성능 시스템에서는 이 과정에서 소요되는 CPU 사이클이 병목이 될 수 있다.10

이를 해결하기 위해 DDS는 인스턴스 핸들(Instance Handle) 패턴을 제공한다. 이는 RDBMS에서 쿼리 계획을 캐싱하거나, 파일 시스템에서 파일 디스크립터(File Descriptor)를 사용하는 원리와 유사하다.

4.1 핸들 기반 쓰기 프로세스

  1. 사전 등록 (Pre-registration): 데이터 전송 루프에 진입하기 전에, register_instance()를 호출하여 인스턴스를 미리 등록한다. 이때 미들웨어는 키 해시 계산과 메모리 할당을 수행하고, 해당 메모리 위치를 가리키는 DDS_InstanceHandle_t를 반환한다.
// 키 필드만 설정된 샘플 데이터 준비
FlightData key_holder;
key_holder.flight_id = 101;

// 인스턴스 등록 및 핸들 획득
DDS_InstanceHandle_t handle_101 = writer->register_instance(key_holder);
  1. 핸들을 이용한 고속 쓰기: 실제 데이터를 보낼 때, write() 함수의 두 번째 인자로 획득한 핸들을 넘겨준다.
FlightData sample;
sample.flight_id = 101;
sample.altitude = 30000;

// 핸들을 사용하여 내부 검색 과정 생략 (O(1) 접근)
writer->write(sample, handle_101);

이 경우 미들웨어는 키 처리를 건너뛰고 핸들이 가리키는 메모리 공간에 즉시 접근하여 데이터를 업데이트한다. 벤치마크 결과에 따르면, 키 구조가 복잡할수록(예: 긴 문자열 복합 키) 핸들 사용 시의 성능 향상 폭이 크며, 단순 정수형 키에서는 그 차이가 미미할 수 있다.10

4.2 핸들 사용 시의 안전성 및 제약사항

핸들 사용은 강력하지만 위험 요소도 존재한다.

  • 핸들의 유효 범위: 핸들은 해당 핸들을 발급한 DataWriter 인스턴스 내에서만 유효하다. 다른 DataWriter나 DataReader에서는 이 핸들 값을 사용할 수 없다. 로컬 메모리 주소에 대한 추상화이기 때문이다.
  • 일관성 책임: 성능 최적화를 위해, 대부분의 DDS 구현체는 write(data, handle) 호출 시 data에 들어있는 키 값과 handle이 가리키는 키 값이 실제로 일치하는지 검사하지 않는다(Unchecked). 만약 개발자의 실수로 flight_id=101인 데이터에 handle_202를 넣어서 보내면, 시스템 내부적으로 데이터 꼬임이 발생하거나 의도치 않은 인스턴스가 덮어씌워질 수 있다.10 따라서 핸들과 키의 매핑 관리는 어플리케이션 레벨에서 매우 엄격하게 캡슐화되어야 한다.

4.3 해시 충돌(Hash Collision)과 내부 처리

DDS는 일반적으로 키 식별을 위해 128비트 MD5 해시 등을 사용한다. 이론적으로 해시 충돌(서로 다른 키가 같은 해시 값을 가지는 현상)의 가능성이 존재한다. 그러나 128비트 공간에서의 충돌 확률은 천문학적으로 낮아(“데이터 센터에 운석이 떨어질 확률보다 낮다“고 비유됨), 실용적인 엔지니어링에서는 무시할 수 있는 수준이다.23 하지만 극도로 민감한 시스템이나 키 공간이 무한한 경우에는 이러한 확률적 특성을 인지하고 있어야 하며, 일부 구현체는 충돌 방지를 위한 추가적인 설정이나 원본 키 비교 옵션을 제공하기도 한다.

5. QoS 정책과 인스턴스 자원 관리

키를 사용하여 인스턴스를 무한히 생성할 수 있다는 점은 양날의 검이다. 관리되지 않는 인스턴스 증가는 메모리 고갈(Out of Memory)을 초래하여 시스템을 붕괴시킬 수 있다. DDS는 이를 제어하기 위해 RESOURCE_LIMITSHISTORY QoS 정책을 통해 인스턴스별 자원 할당을 엄격하게 관리한다.24

5.1 자원 제한 정책 (RESOURCE_LIMITS)

이 정책은 DataWriter나 DataReader가 관리할 수 있는 물리적 메모리의 한계를 규정한다.

  • max_instances: 관리 가능한 최대 인스턴스 수를 제한한다. 임베디드 시스템에서는 이 값을 고정하여 부팅 시점에 필요한 메모리를 정적으로 할당(Pre-allocation)받도록 함으로써, 런타임 중 동적 메모리 할당으로 인한 지연(Latency)과 단편화(Fragmentation)를 방지하는 것이 일반적인 패턴이다.24
  • max_samples_per_instance: 하나의 인스턴스가 가질 수 있는 최대 샘플 수를 제한한다. 이는 특정 인스턴스에 데이터가 폭주하더라도 다른 인스턴스를 위한 버퍼 공간을 보호하는 역할을 한다.

인스턴스 교체(Replacement) 전략: 만약 max_instances 한도에 도달했는데 새로운 키를 가진 데이터가 들어오면 어떻게 될까? 기본적으로는 오류(Error)를 반환하거나 데이터를 거부한다. 그러나 일부 구현체는 가장 오랫동안 업데이트되지 않은 인스턴스나 이미 unregister된 인스턴스의 자원을 회수하여 새 인스턴스에 할당하는 교체 정책을 지원하기도 한다.27

5.2 인스턴스별 히스토리 관리 (HISTORY)

DDS의 HISTORY QoS(KEEP_LAST 또는 KEEP_ALL)는 토픽 전체가 아니라 **각 인스턴스별(Per Instance)**로 독립적으로 적용된다는 점이 매우 중요하다.

  • KEEP_LAST (depth=10): “각 비행기(인스턴스)마다 최근 10개의 위치 정보를 저장하라“는 의미이다. 비행기가 1,000대라면 총 10,000개의 샘플이 저장될 수 있다.

  • 메모리 사용량 예측: 총 메모리 사용량 \approx max_instances \times history_depth \times sample_size.

개발자는 이 공식을 사용하여 시스템의 메모리 요구사항을 정확히 산정해야 한다. 단순히 토픽 단위로 생각하면 예상치 못한 메모리 부족 사태를 겪을 수 있다.24

5.3 소유권(OWNERSHIP)과 인스턴스 단위 중재

OWNERSHIP QoS가 EXCLUSIVE로 설정된 경우, 데이터 쓰기 권한의 중재(Arbitration) 또한 인스턴스 단위로 이루어진다.

  • 시나리오: 주 시스템(Writer A, Strength 10)과 백업 시스템(Writer B, Strength 5)이 동시에 가동 중이다.
  • 동작: 평소에는 Writer A가 데이터를 보내므로 Writer B의 데이터는 무시된다. 그러나 A가 특정 센서(Instance K1)의 고장을 감지하여 해당 인스턴스만 unregister하면, DDS는 즉시 해당 인스턴스(K1)에 대해서만 소유권을 Writer B로 넘긴다. 다른 정상적인 센서(Instance K2, K3…)들은 여전히 A가 소유권을 가진다.
  • 효과: 시스템 전체를 절체(Fail-over)할 필요 없이, 문제가 발생한 데이터 스트림 단위로 정밀한 이중화(Redundancy) 제어가 가능하다.28

6. 대규모 시스템을 위한 키 설계 패턴과 최적화

성공적인 DDS 시스템 구축을 위해서는 비즈니스 요구사항과 시스템 성능 특성을 고려한 전략적인 키 설계가 필수적이다. 잘못된 키 선정은 ’인스턴스 폭발(Instance Explosion)’이나 조회 성능 저하를 일으킬 수 있다.13

6.1 자연 키(Natural Key) 대 대리 키(Surrogate Key)

데이터 모델링 시 가장 먼저 부딪히는 딜레마이다.

  • 자연 키 (Natural Key): 도메인 데이터 자체에 존재하는 속성(예: 주민등록번호, 차량 번호, 이메일)을 키로 사용한다.
  • 장점: 데이터의 의미를 직관적으로 이해할 수 있고, 별도의 매핑 테이블 없이 조회 가능하다. 디버깅이 용이하다.
  • 단점: 비즈니스 로직 변경에 따라 키 값이 변할 수 있다(예: 차량 번호판 변경). DDS 키는 불변이어야 하므로, 값이 바뀌면 기존 인스턴스를 폐기하고 새로 생성해야 하는데, 이는 데이터 연속성을 끊는다. 또한 문자열 등 길이가 긴 경우가 많아 해싱 및 전송 오버헤드가 크다.
  • 대리 키 (Surrogate Key): 시스템이 임의로 생성한 식별자(예: UUID, Auto-increment ID)를 사용한다.
  • 장점: 불변성이 보장되며, 64비트 정수 등으로 컴팩트하게 설계하여 성능을 극대화할 수 있다.
  • 단점: 실제 데이터와의 매핑 관계를 별도로 관리해야 한다.
  • 권장: 대규모 고성능 시스템에서는 내부적으로 **대리 키(정수형)**를 사용하여 통신 효율을 높이고, 어플리케이션 계층에서 자연 키와 매핑하는 하이브리드 전략이 권장된다.13

6.2 인스턴스 과다(Too Many Instances) 문제와 굵은 입자(Coarse-Grained) 키 전략

수백만, 수천만 개의 인스턴스가 발생하는 경우(예: 픽셀 단위의 데이터 처리, 전 국민 대상 데이터), 미들웨어의 인스턴스 관리 오버헤드가 급증한다. 이때는 키의 단위를 조절해야 한다.6

  • 문제: 개별 입자 하나하나를 인스턴스로 만들면 메타데이터 메모리가 페이로드보다 커지는 배보다 배꼽이 더 큰 상황이 발생한다.
  • 해결책 (Coarse-Grained Key): 개별 객체를 인스턴스로 만드는 대신, 객체들의 **그룹(Group)이나 버킷(Bucket)**을 인스턴스로 정의한다.
  • 예: 100만 개의 센서를 개별 인스턴스로 만드는 대신, ’지역(Region)’을 키로 설정하여 1,000개의 인스턴스만 만든다. 각 인스턴스의 데이터 샘플은 해당 지역에 속한 1,000개 센서의 값들을 배열(Sequence)로 담아서 한 번에 전송한다.
  • 이 방식은 미들웨어의 관리 부담을 줄이고 처리량(Throughput)을 높이는 효과적인 패턴이다.

6.3 데이터베이스와의 동기화 패턴

DDS 인스턴스는 RDBMS의 Row와 1:1로 매핑되기 가장 좋은 구조이다.

  • Primary Key 매핑: DDS 키 필드를 DB 테이블의 Primary Key로 사용한다.
  • 상태 동기화: DDS의 ALIVE 상태는 DB의 INSERT/UPDATE에, NOT_ALIVE_DISPOSED 상태는 DB의 DELETE 또는 IsDeleted=true 플래그 업데이트에 매핑한다. 이를 통해 실시간 분산 데이터와 영속적 저장소 간의 정합성을 유지하는 아키텍처를 손쉽게 구현할 수 있다.3

7. 결론 및 시사점

DDS에서 키(Key) 설정은 단순한 데이터 필드 정의가 아니다. 그것은 거대한 글로벌 데이터 공간을 어떻게 논리적으로 구획하고 관리할 것인가에 대한 아키텍처적 선언이다. @key 어노테이션 하나가 데이터의 라우팅 경로부터 메모리 구조, QoS 적용 범위, 그리고 장애 허용 범위까지 결정짓는다.

개발자는 다음의 원칙을 명심해야 한다.

  1. 불변성: 키는 절대 변하지 않는 속성으로 선정하라.
  2. 적절한 입자성: 너무 많은 인스턴스는 시스템을 마비시키고, 너무 적은 인스턴스(Keyless)는 병렬 처리와 필터링의 이점을 앗아간다. 도메인 특성에 맞는 균형점을 찾아라.
  3. 생명주기 관리: register, dispose, unregister의 의미를 명확히 이해하고 의도에 맞게 사용하라. 좀비 인스턴스가 메모리를 잠식하지 않도록 하라.

올바른 키 전략은 DDS라는 강력한 엔진이 단순한 메시지 버스가 아니라, 살아있는 데이터 객체들의 유기적인 생태계로 동작하게 만드는 핵심 열쇠이다. 이를 통해 개발자는 복잡한 분산 시스템의 상태 관리 부담을 미들웨어에 위임하고, 비즈니스 로직에 온전히 집중할 수 있게 된다.

8. 참고 자료

  1. DDS Samples, Instances, and Keys - RTI Community, https://community.rti.com/static/documentation/connext-dds/current/doc/manuals/connext_dds_professional/users_manual/users_manual/DDS_Samples__Instances__and_Keys.htm
  2. The Data Distribution Service, https://www.cs.wm.edu/~dcschmidt/PDF/dds-sos.pdf
  3. 3.5.1. Topics, keys and instances - eProsima Fast DDS, https://fast-dds.docs.eprosima.com/en/2.x/fastdds/dds_layer/topic/instances.html
  4. Tutorial on the OMG Data Distribution Service, http://www.dre.vanderbilt.edu/~gokhale/OMG_RTWS06/00-T1-1_Giddings.pdf
  5. 3.5.1. Topics, keys and instances - eProsima Fast DDS, https://fast-dds.docs.eprosima.com/en/3.x/fastdds/dds_layer/topic/instances.html
  6. Use Keyed Topics | Data Distribution Service (DDS) Community RTI Connext Users, https://community.rti.com/best-practices/use-keyed-topics
    1. Extensible and Dynamic Topic Types for DDS annotation support — IDL PreProcessor Guide - ZettaScale Technology, https://download.zettascale.online/www/docs/OpenSplice/v7/html/ospl/IDLPreProcGuide/topictypes.html
  7. 20.3.9 Using Builtin Annotations - RTI Community, https://community.rti.com/static/documentation/connext-dds/current/doc/manuals/connext_dds_professional/users_manual/users_manual/Using_BuiltinAnnotations.htm
  8. Extensible and Dynamic Topic Types for DDS - Object Management Group (OMG), https://www.omg.org/spec/DDS-XTypes/1.1/PDF
  9. Register the Instance and Use the InstanceHandle When Writing for Better Performance | Data Distribution Service (DDS) Community RTI Connext Users, https://community.rti.com/best-practices/register-instance-and-use-instancehandle-when-writing-better-performance
    1. Defining a data type via IDL - 3.4.1 - eProsima Fast DDS, https://fast-dds.docs.eprosima.com/en/3.4.x/fastddsgen/dataTypes/dataTypes.html
  10. What syntax and datatypes are supported in IDL - Eclipse Cyclone DDS - FAQ, https://cyclonedds.io/content/guides/supported-idl.html
  11. Why Surrogate Keys Win: A Practical Guide to Database Key Selection - Liam ERD, https://liambx.com/blog/why-surrogate-keys-win
  12. 20.1.4.4. InstanceStateKind - 3.4.1 - eProsima Fast DDS, https://fast-dds.docs.eprosima.com/en/3.x/fastdds/api_reference/dds_pim/subscriber/instancestatekind.html
  13. Advanced Tutorial Using QoS to Solve Real-World Problems - DDS Foundation, https://www.dds-foundation.org/sites/default/files/DDS_Advanced_Ttutorial_00-T5_Hunt-revised.pdf
  14. CoreDX DDS Sample and Instance Management - Twin Oaks Computing, Inc, https://www.twinoakscomputing.com/wp/CoreDX_DDS_Instance_Management.pdf
  15. 43.8 Accessing and Managing Instances (Working with Keyed Data Types) - RTI Community, https://community.rti.com/static/documentation/connext-dds/current/doc/manuals/connext_dds_professional/users_manual/users_manual/AccessingManagingInstances.htm
  16. Understanding Instance States | Data Distribution Service (DDS) Community RTI Connext Users, https://community.rti.com/kb/understanding-instance-states
  17. WRITER_DATA_LIFECYCLE QoS Parameter [DDS Foundation Wiki], https://www.omgwiki.org/ddsf/doku.php?id=ddsf:public:guidebook:06_append:02_quality_of_service:writer_data_lifecycle
  18. RTI Connext Traditional C++ API: FooDataWriter Class Reference - RTI Community, https://community.rti.com/static/documentation/connext-dds/current/doc/api/connext_dds/api_cpp/structFooDataWriter.html
  19. Invalid data with DDS_NOT_ALIVE_DISPOSED_INSTANCE_STATE received #148 - GitHub, https://github.com/eclipse-cyclonedds/cyclonedds/issues/148
  20. 34.14 Managing Instances (Working with Keyed Data Types) - RTI Community, https://community.rti.com/static/documentation/connext-dds/current/doc/manuals/connext_dds_professional/users_manual/users_manual/Managing_Data_Instances__Working_with_Ke.htm
  21. Hash keys, the unsung hero of data warehousing - Part 2 - TPXimpact, https://www.tpximpact.com/knowledge-hub/blogs/tech/hash-keys-data-warehousing-2
  22. RESOURCE_LIMITS QosPolicy - RTI Community, https://community.rti.com/static/documentation/connext-dds/current/doc/manuals/connext_dds_professional/users_manual/users_manual/RESOURCE_LIMITS_QosPolicy.htm
  23. Quality of Service (QoS) policies are sets of requested policies for how - entities - OpenDDS, https://opendds.readthedocs.io/en/master/devguide/quality_of_service.html
  24. RTI Connext DDS Micro C API: max_instances, https://community.rti.com/static/documentation/connext-micro/2.4.14/doc/api_c/html/group__DDSUserManuals__ResourceModule__dwqos__max__instances.html
  25. Instance Resources, Dispose, and Unregister | Data Distribution Service (DDS) Community RTI Connext Users, https://community.rti.com/content/forum-topic/instance-resources-dispose-and-unregister
  26. 59.17 OWNERSHIP QosPolicy - RTI Community, https://community.rti.com/static/documentation/connext-dds/current/doc/manuals/connext_dds_professional/users_manual/users_manual/OWNERSHIP_QosPolicy.htm
  27. OWNERSHIP QoS Parameter [DDS Foundation Wiki], https://www.omgwiki.org/ddsf/doku.php?id=ddsf:public:guidebook:06_append:02_quality_of_service:ownership
  28. How do you use tradeoffs to improve performance? - AWS Well-Architected Framework, https://wa.aws.amazon.com/wellarchitected/2020-07-02T19-33-23/wat.question.PERF_8.en.html
  29. MuscleParseNet: A Novel Framework for Parsing Muscles of Drosophila Larva in Light-Sheet Fluorescence Microscopy Images - IEEE Xplore, https://ieeexplore.ieee.org/iel7/76/10550083/10339386.pdf